﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UTSDAL.LINQSQL.Model;
using UTSDAL.LINQSQL.Utils;
using UTSDAL.LINQSQL.Exceptions;

namespace UTSDAL.LINQSQL.DAO
{
    public class RoomDAO:GenericDAO<Room,Guid>
    {

        /// <summary>
        /// locates a room  by its name
        /// </summary>
        /// <param name="roomName"></param>
        /// <returns></returns>
        public Room findRoomByRoomName(String roomName)
        {
            var query = from r in base.Context.Rooms
                        where r.RoomName.Equals(roomName)
                        select r;

            return query.SingleOrDefault<Room>();
        }

        /// <summary>
        /// retrieves a List of rooms that have facilities required by a module event
        /// </summary>
        /// <param name="moduleEvent"></param>
        /// <returns></returns>
        public List<Room> getRoomForModuleEvent(ModuleEvent moduleEvent)
        {

            
            var roomQuery = from r in base.Context.DepartmentRooms
                            where r.Department.Equals(moduleEvent.Cohort.Department)
                            select r.Room;


            
            return roomQuery.ToList<Room>();
        }


        /// <summary>
        /// checks whether room is allocated during between start and end time of the event
        /// to any other module on that particular day.
        /// NOTE: This method is in TimetableEventDAO also and should be merged in a CommonDAO
        /// </summary>
        /// <param name="timetableEvent"></param>
        /// <returns></returns>
        public Boolean isTimetableEventExistingForAnyModule(TimetableEvent timetableEvent)
        {

            var timetableEventExistingVar = from e in base.Context.TimetableEvents
                                            where e.Semester.Equals(timetableEvent.Semester)
                                            && e.CourseDay.Equals(timetableEvent.CourseDay)
                                            && e.EndTime >= timetableEvent.StartTime
                                            && e.Room.Equals(timetableEvent.Room)
                                            select e;

            List<TimetableEvent> existingTimetableEvents = (List<TimetableEvent>)timetableEventExistingVar.ToList<TimetableEvent>();

            return existingTimetableEvents.Count > 0;
        }




        public List<Room> getRoomForTimetableEvent(TimetableEvent timetableEvent)
        {
            List<Room> roomsCorrespondingToModuleEvent = getRoomForModuleEvent(timetableEvent.ModuleEvent);

            List<Room> roomsCorrespondingToTimetableEvent = new List<Room>();


            foreach(Room room in roomsCorrespondingToModuleEvent)
            {

                timetableEvent.Room = room;
                

                //checking if the room which would be allocated to the timetable event
                // is not already occupied during that time and that day
                Boolean exists = isTimetableEventExistingForAnyModule(timetableEvent);

                if(!exists)
                {
                    roomsCorrespondingToTimetableEvent.Add(room);
                }
            }

            // Deallocating Room as we need to check before
            timetableEvent.Room = null;
            
            return
                roomsCorrespondingToTimetableEvent;
        }
        


        public TimetableEvent allocateRoomToTimetableEvent(TimetableEvent timetableEvent)
        {

            foreach (Room room in getRoomForTimetableEvent(timetableEvent))
            {

                if (timetableEvent.ModuleEvent.ModuleEventFacilities.Count == 0)
                {
                    timetableEvent.Room = room;
                    break;

                }
                else
                {
                    if (DAOUtils.doesRoomFacilitiesContainModuleEventFacilities(room.RoomFacilities.ToList<RoomFacility>(), timetableEvent.ModuleEvent.ModuleEventFacilities.ToList<ModuleEventFacility>()))
                    {
                        timetableEvent.Room = room;
                        break;
                    }
                }
            }
            
            // if we could not allocated a room, then we invalidate the timetable event by setting to null
            // so that the calling method can recognise that room allocation failed
            if (timetableEvent.Room == null)
            {
                timetableEvent = null;
                throw new RoomNotAvailableException("Room could not be allocated ");
            }

            

            return timetableEvent;
        }


        public Boolean isRoomBusy(TimetableEvent timetableEvent)
        {
            var events = from e in base.Context.TimetableEvents
                        where e.Room.RoomId.Equals(timetableEvent.RoomId)
                        && e.CourseDay.CourseDayId.Equals(timetableEvent.CourseDayId)
                        && timetableEvent.StartTime > e.StartTime
                        && timetableEvent.StartTime < e.EndTime
                        && timetableEvent.Semester.Equals(e.Semester)
                        select e;

            return events.Count<TimetableEvent>() == 0;
        }


    }
}
